﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Media;
using System.IO;
using System.Runtime.InteropServices;

namespace Memorize_Game
{
    enum status { on, off }; 

    internal partial class frm_playboard : Form
    {
        #region Game variables

        List<Image> imageSet = new List<Image>(); Image[] shuffledSet = new Image[16]; Image[] upCardImage = new Image[2]; Image cardDown; static Image[] card_up = new Image[16];
        Random Randomer = new Random();
        bool clickFlag, nomatchFlag, matchFlag;
        int imageSetCount, shuffledSetIndex, cardID; static int ctr; int[] upCardIndex = new int[2]; static int secondCounter, numberOfMatch;
        status[] btn_click = new status[16];
        string[] upCardName = new string[2]; static string[] toTurnDown = new string[2]; static string[] toTakeOff = new string[2];
        frm_help help; frm_topscorer topscore, ts; DialogResult DR;
        Stream cardClickS, matchUpS, menuClickS, clockingS; internal static SoundPlayer cardClick, matchUp, menuClick, Clocking;
        

        internal frm_playboard()
        {
            InitializeComponent();
        }

        private void prepareImagesAndSounds() //store images and sounds from resources to variables
        {
            card_up[0] = Properties.Resources.card_up8; card_up[1] = Properties.Resources.card_up7; card_up[2] = Properties.Resources.card_up6;
            card_up[3] = Properties.Resources.card_up2; card_up[4] = Properties.Resources.card_up1; card_up[5] = Properties.Resources.card_up3;
            card_up[6] = Properties.Resources.card_up4; card_up[7] = Properties.Resources.card_up5; cardDown = Properties.Resources.card_down;
            card_up[8] = Properties.Resources.card_up8; card_up[9] = Properties.Resources.card_up7; card_up[10] = Properties.Resources.card_up6;
            card_up[11] = Properties.Resources.card_up2; card_up[12] = Properties.Resources.card_up1; card_up[13] = Properties.Resources.card_up3;
            card_up[14] = Properties.Resources.card_up4; card_up[15] = Properties.Resources.card_up5;
            card_up[0].Tag = card_up[8].Tag = 8; card_up[1].Tag = card_up[9].Tag = 7; card_up[2].Tag = card_up[10].Tag = 6; card_up[3].Tag = card_up[11].Tag = 2;
            card_up[4].Tag = card_up[12].Tag = 1; card_up[5].Tag = card_up[13].Tag = 3; card_up[6].Tag = card_up[14].Tag = 4; card_up[7].Tag = card_up[15].Tag = 5;

            cardClickS = Properties.Resources.cardclick; matchUpS = Properties.Resources.matchup; menuClickS = Properties.Resources.menuclick;
            clockingS = Properties.Resources.clocktick; btn_sound.Text = "Sound ON"; setVolume(5);
            cardClick = new SoundPlayer(cardClickS); matchUp = new SoundPlayer(matchUpS); menuClick = new SoundPlayer(menuClickS); Clocking = new SoundPlayer(clockingS);
        }

        #endregion

        #region Playboard events

        private void loadImageSet() //load images from resources to a list of images variable
        {
            for (int i = 0; i < 16; i++)
            {
                imageSet.Add(card_up[i]);
            }

            this.allCardsDown();

            for (int i = 0; i < 16; i++) //make all cards clickable
            {
                btn_click[i] = status.on;
            }
        }

        private void allCardsDown() //set all cards on the playboard face down
        {
            foreach (PictureBox PB in tLP_playboard.Controls) 
            {
                PB.Image = cardDown;
            }
        }

        private void shuffleSetImages() //shuffle images and store them in an array of images
        {
            for (int i = 0; i < 16; i++)
            {
                imageSetCount = imageSet.Count - 1; shuffledSetIndex = Randomer.Next(0, imageSetCount);
                shuffledSet[i] = imageSet[shuffledSetIndex]; imageSet.RemoveAt(shuffledSetIndex);
            }
        }

        private void setPlayboardState(bool switcher) //disable or enable all cards
        {
            foreach (PictureBox PB in tLP_playboard.Controls)
            {
                PB.Enabled = switcher;
            }
        }

        private void frm_playboard_Load(object sender, EventArgs e)
        {
            this.prepareImagesAndSounds(); this.setPlayboardState(false); btn_pauzres.Enabled = false;
        }

        private void newGame() //set parameters for a new game
        {
            this.timerEvents(status.off);
            DR = MessageBox.Show("Would you like to start a new game ?", "New Game", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
            if (DR == DialogResult.Yes)
            {
                this.loadImageSet(); this.shuffleSetImages(); ctr = numberOfMatch = 0; secondCounter = 0; this.setPlayboardState(true); btn_pauzres.Text = "Pause";
                toTurnDown[0] = toTurnDown[1] = toTakeOff[0] = toTakeOff[1] = ""; upCardIndex[0] = upCardIndex[1] = 1; nomatchFlag = matchFlag = false;
                this.timerEvents(status.on); btn_pauzres.Enabled = true;
            }
            else { this.onResumeCheck(); }
        }

        #endregion

        #region Game workings

        private void cardTurnerUp(PictureBox PB) //turns the card upside down when clicked to display the image
        {
            PB.Image = shuffledSet[cardID]; this.matchController(PB.Name);
        }

        private void matchController(string cN) //checks for a match in images revealed, if any, then removes the cards from the board
        {
            switch (ctr)
            {
                case 0:
                    this.cardMatchingActions(); nomatchFlag = matchFlag = false;
                    upCardImage[0] = shuffledSet[cardID]; upCardIndex[0] = cardID; btn_click[upCardIndex[0]] = status.off; upCardName[0] = cN; ++ctr; break;
                case 1:
                    upCardImage[1] = shuffledSet[cardID]; upCardIndex[1] = cardID; btn_click[upCardIndex[1]] = status.off; upCardName[1] = cN; ++ctr; break;
            }

            if (ctr == 2)
            {
                if (upCardImage[0].Tag == upCardImage[1].Tag)
                {
                    ++numberOfMatch; toTakeOff[0] = upCardName[0]; toTakeOff[1] = upCardName[1]; matchFlag = true;
                    if (numberOfMatch == 8) { this.checkWin(); }
                }
                else
                {
                    toTurnDown[0] = upCardName[0]; toTurnDown[1] = upCardName[1]; nomatchFlag = true;
                }
                ctr = 0;
            }
        }

        private void cardMatchingActions() //actions to be taken when there is and there is no match after revealing two cards
        {
            if (nomatchFlag) //when there isn't any match
            {
                foreach (PictureBox PB in tLP_playboard.Controls)
                {
                    if (PB.Name == toTurnDown[0] || PB.Name == toTurnDown[1])
                    {
                        PB.Image = cardDown;
                    }
                }
                btn_click[upCardIndex[0]] = btn_click[upCardIndex[1]] = status.on;
            }
            if (matchFlag) //when there is a match
            {
                foreach (PictureBox PB in tLP_playboard.Controls)
                {
                    if (PB.Name == toTakeOff[0] || PB.Name == toTakeOff[1])
                    {
                        PB.Image = null;
                    }
                }
                matchUp.Play();
            }
        }

        private bool checkClickStatus(status[] bc, int ID) //check whether a card is clickable or not
        {
            clickFlag = true; if (bc[ID] == status.off) { clickFlag = false; } return clickFlag;
        }

        private void timer_Tick(object sender, EventArgs e) //timer counting
        {
            if (secondCounter < 100)
            {
                ++secondCounter; Clocking.Play();
                lbl_timelapsed.Text = Convert.ToString(secondCounter);
            }
            else
            {
                this.checkWin();
            }
        }

        private void checkWin() //check for a win
        {
            this.timerEvents(status.off);
            if (numberOfMatch.Equals(8) && secondCounter != 100)
            {
                MessageBox.Show("Congratulations, You won !"); ts=new frm_topscorer('S');
                if (secondCounter < ts.lowesTime)
                {
                    ts.lowesTime = int.Parse(lbl_timelapsed.Text); ts.ShowDialog();
                }
                this.playAgain();
            }
            else
            {
                MessageBox.Show("Sorry, You lost !"); this.playAgain();
            }
        }

        private void playAgain()
        {
            DR = MessageBox.Show("Would you like to play again ?", "New game", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
            if (DR == DialogResult.Yes)
            {
                this.newGame();
            }
            else
            {
                this.allCardsDown(); this.setPlayboardState(false); btn_pauzres.Enabled = false; lbl_timelapsed.Text = "000";
            }
        }

        private void timerEvents(status gS) //control the timer states
        {
            switch (gS)
            {
                case status.on:
                    timer.Enabled = true; break;
                case status.off:
                    timer.Enabled = false; break;
            }
        }

        private void onResumeCheck()
        {
            if (btn_pauzres.Text != "Resume" && btn_pauzres.Enabled) { this.timerEvents(status.on); }
        }

        #endregion

        #region Card click events

        private void _0_Click(object sender, EventArgs e)
        {
            cardClick.Play(); cardID = 0; if (this.checkClickStatus(btn_click, cardID)) { this.cardTurnerUp(_0); }
        }

        private void _1_Click(object sender, EventArgs e)
        {
            cardClick.Play(); cardID = 1; if (this.checkClickStatus(btn_click, cardID)) { this.cardTurnerUp(_1); }
        }

        private void _2_Click(object sender, EventArgs e)
        {
            cardClick.Play(); cardID = 2; if (this.checkClickStatus(btn_click, cardID)) { this.cardTurnerUp(_2); }
        }

        private void _3_Click(object sender, EventArgs e)
        {
            cardClick.Play(); cardID = 3; if (this.checkClickStatus(btn_click, cardID)) { this.cardTurnerUp(_3); }
        }

        private void _4_Click(object sender, EventArgs e)
        {
            cardClick.Play(); cardID = 4; if (this.checkClickStatus(btn_click, cardID)) { this.cardTurnerUp(_4); }
        }

        private void _5_Click(object sender, EventArgs e)
        {
            cardClick.Play(); cardID = 5; if (this.checkClickStatus(btn_click, cardID)) { this.cardTurnerUp(_5); }
        }

        private void _6_Click(object sender, EventArgs e)
        {
            cardClick.Play(); cardID = 6; if (this.checkClickStatus(btn_click, cardID)) { this.cardTurnerUp(_6); }
        }

        private void _7_Click(object sender, EventArgs e)
        {
            cardClick.Play(); cardID = 7; if (this.checkClickStatus(btn_click, cardID)) { this.cardTurnerUp(_7); }
        }

        private void _8_Click(object sender, EventArgs e)
        {
            cardClick.Play(); cardID = 8; if (this.checkClickStatus(btn_click, cardID)) { this.cardTurnerUp(_8); }
        }

        private void _9_Click(object sender, EventArgs e)
        {
            cardClick.Play(); cardID = 9; if (this.checkClickStatus(btn_click, cardID)) { this.cardTurnerUp(_9); }
        }

        private void _10_Click(object sender, EventArgs e)
        {
            cardClick.Play(); cardID = 10; if (this.checkClickStatus(btn_click, cardID)) { this.cardTurnerUp(_10); }
        }

        private void _11_Click(object sender, EventArgs e)
        {
            cardClick.Play(); cardID = 11; if (this.checkClickStatus(btn_click, cardID)) { this.cardTurnerUp(_11); }
        }

        private void _12_Click(object sender, EventArgs e)
        {
            cardClick.Play(); cardID = 12; if (this.checkClickStatus(btn_click, cardID)) { this.cardTurnerUp(_12); }
        }

        private void _13_Click(object sender, EventArgs e)
        {
            cardClick.Play(); cardID = 13; if (this.checkClickStatus(btn_click, cardID)) { this.cardTurnerUp(_13); }
        }

        private void _14_Click(object sender, EventArgs e)
        {
            cardClick.Play(); cardID = 14; if (this.checkClickStatus(btn_click, cardID)) { this.cardTurnerUp(_14); }
        }

        private void _15_Click(object sender, EventArgs e)
        {
            cardClick.Play(); cardID = 15; if (this.checkClickStatus(btn_click, cardID)) { this.cardTurnerUp(_15); }
        }

#endregion

        #region Menu buttons click events

        private void btn_pauzres_Click(object sender, EventArgs e) //pause resume click event
        {
            menuClick.Play();
            if (btn_pauzres.Text == "Pause")
            {
                this.timerEvents(status.off);
                this.setPlayboardState(false);
                btn_pauzres.Text = "Resume";
            }
            else
            {
                btn_pauzres.Text = "Pause";
                this.setPlayboardState(true);
                this.timerEvents(status.on);
            }
        }

        private void btn_help_Click(object sender, EventArgs e) // help button click event
        {
            menuClick.Play(); this.timerEvents(status.off); help = new frm_help(); DR = help.ShowDialog();
            if (DR == DialogResult.OK)
            {
                this.onResumeCheck(); 
            }
        }

        private void btn_newgame_Click(object sender, EventArgs e) //new game button click event
        {
            menuClick.Play(); this.newGame(); 
        }

        private void btn_topscore_Click(object sender, EventArgs e) //top score button click event
        {
            menuClick.Play(); this.timerEvents(status.off); topscore = new frm_topscorer('V'); DR = topscore.ShowDialog();
            if (DR == DialogResult.OK)
            {
                this.onResumeCheck(); 
            }
        }

        private void btn_sound_Click(object sender, EventArgs e) //sound button click event
        {
            if (btn_sound.Text == "Sound ON")
            {
                setVolume(0); btn_sound.Text = "Sound OFF";
            }
            else
            {
                setVolume(5); btn_sound.Text = "Sound ON";
            }
        }

        #endregion

        #region Menu buttons clicks visual effects

        private void btn_newgame_MouseDown(object sender, MouseEventArgs e)
        {
            btn_newgame.ForeColor = Color.OrangeRed;
        }

        private void btn_newgame_MouseUp(object sender, MouseEventArgs e)
        {
            btn_newgame.ForeColor = Color.DarkOrange;

        }

        private void btn_pauzres_MouseDown(object sender, MouseEventArgs e)
        {
            btn_pauzres.ForeColor = Color.OrangeRed;
        }

        private void btn_pauzres_MouseUp(object sender, MouseEventArgs e)
        {
            btn_pauzres.ForeColor = Color.DarkOrange;
        }

        private void btn_topscore_MouseDown(object sender, MouseEventArgs e)
        {
            btn_topscore.ForeColor = Color.OrangeRed;
        }

        private void btn_topscore_MouseUp(object sender, MouseEventArgs e)
        {
            btn_topscore.ForeColor = Color.DarkOrange;
        }

        private void btn_help_MouseDown(object sender, MouseEventArgs e)
        {
            btn_help.ForeColor = Color.OrangeRed;
        }

        private void btn_help_MouseUp(object sender, MouseEventArgs e)
        {
            btn_help.ForeColor = Color.DarkOrange;
        }

        private void btn_sound_MouseDown(object sender, MouseEventArgs e)
        {
            btn_sound.ForeColor = Color.OrangeRed;
        }

        private void btn_sound_MouseUp(object sender, MouseEventArgs e)
        {
            btn_sound.ForeColor = Color.DarkOrange;
        }

        #endregion

        #region Sound Control

        [DllImport("winmm.dll")]
        private static extern int waveOutSetVolume(IntPtr hwo, uint dwVolume);

        public static void setVolume(int volume)
        {
            int NewVolume = ((ushort.MaxValue / 10) * volume);
            uint NewVolumeAllChannels = (((uint)NewVolume & 0x0000ffff) | ((uint)NewVolume << 16));
            waveOutSetVolume(IntPtr.Zero, NewVolumeAllChannels);
        }

        #endregion
    }
}
